package com.briup.server.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.briup.common.constant.SystemConstant;
import com.briup.common.exception.ServiceException;
import com.briup.common.response.ResultCode;
import com.briup.common.utils.AssertUtil;
import com.briup.common.utils.BeanUtil;
import com.briup.server.domain.bean.Menu;
import com.briup.server.domain.dto.MenuDTO;
import com.briup.server.domain.dto.MenuMoveDTO;
import com.briup.server.domain.dto.MenuMoveDetailDTO;
import com.briup.server.domain.dto.MenuQueryDTO;
import com.briup.server.domain.vo.MenuVO;
import com.briup.server.mapper.MenuMapper;
import com.briup.server.service.MenuService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.List;

@Service
@RequiredArgsConstructor
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu>
        implements MenuService {

    private final MenuMapper menuMapper;

    @Override
    public List<MenuVO> listMenus() {
        return menuMapper.queryMenuVOs();
    }

    @Override
    public Page<MenuVO> pageMenus(MenuQueryDTO menuQueryDTO) {
        Page<Menu> page = new Page<>(menuQueryDTO.getPage(), menuQueryDTO.getSize());
        // 构建查询条件
        LambdaQueryWrapper<Menu> wrapper = Wrappers.<Menu>lambdaQuery()
                .eq(Menu::getType, 0)
                .like(StringUtils.hasText(menuQueryDTO.getName()),
                        Menu::getName, menuQueryDTO.getName());
        // 查询符合条件的父目录的列表
        page(page, wrapper);
        // 转换page
        Page<MenuVO> menuVOPage = BeanUtil.convertPage(page, MenuVO.class);

        // 给菜单设置子菜单或按钮；需要递归查询（menuMapper中设置的自定义结果集使用的就是嵌套查询）
        menuVOPage.getRecords().forEach(this::setChildren);

        // 查询符合条件的父目录的条数
        Long count = menuMapper.selectCount(wrapper);
        menuVOPage.setTotal(count);
        return menuVOPage;
    }

    @Override
    public void saveOrUpdateMenu(MenuDTO menuDTO) {
        // 先校验菜单是否已经存在
        checkMenuExists(menuDTO);
        // 转换为Menu实体
        Menu menu = BeanUtil.convert(menuDTO, Menu.class);
        Menu parentMenu = getOne(Wrappers.lambdaQuery(Menu.class)
                .eq(Menu::getId, menu.getParentId()));
        AssertUtil.isTrue(parentMenu != null, ResultCode.MENU_PARENT_IS_NOT_EXISTS);
        if (menu.getId() == null && parentMenu.getType()
                .equals(SystemConstant.MenuTypeConstant.BUTTON.getMenuTypeId())) {
            throw new ServiceException(ResultCode.MENU_TYPE_ERROR);
        }
        // 新增或修改菜单
        saveOrUpdate(menu);
    }

    @Transactional
    @Override
    public void moveMenu(MenuMoveDTO menuMoveDTO) {
        // 校验菜单是否已经存在
        checkMenuExists(menuMoveDTO.getSourceMenu());
        checkMenuExists(menuMoveDTO.getTargetMenu());
        // 校验目标菜单和原菜单是否在同一个父菜单下
        AssertUtil.isTrue(menuMoveDTO.getSourceMenu().getParentId()
                        .equals(menuMoveDTO.getTargetMenu().getParentId()),
                ResultCode.MENU_PARENT_ID_ERROR);
        // 更新菜单顺序
        List<Menu> menuList = List.of(
                BeanUtil.convert(menuMoveDTO.getSourceMenu(), Menu.class),
                BeanUtil.convert(menuMoveDTO.getTargetMenu(), Menu.class));
        updateBatchById(menuList);
    }

    @Override
    public void removeMenu(Long id) {
        // 校验菜单是否存在
        Menu menu = getById(id);
        if (menu == null) {
            // 菜单不存在，直接返回；因为本身就是要删除菜单
            return;
        }
        // 校验是否存在子菜单或按钮
        LambdaQueryWrapper<Menu> wrapper = Wrappers.<Menu>lambdaQuery()
                .eq(Menu::getParentId, id);
        // 校验是否存在子菜单或按钮
        long count = count(wrapper);
        AssertUtil.isTrue(count == 0, ResultCode.MENU_HAS_CHILDREN);
        // 删除菜单
        removeById(id);
    }

    private void checkMenuExists(MenuMoveDetailDTO menuDTO) {
        // 校验菜单是否已经存在
        Menu menu = getById(menuDTO.getId());
        AssertUtil.isTrue(menu != null, ResultCode.MENU_IS_NOT_EXISTS);
        // 新增菜单或修改菜单时，确保父菜单ID是存在的
        LambdaQueryWrapper<Menu> wrapper = Wrappers.<Menu>lambdaQuery()
                // 如果传入的父菜单ID不为0，说明是子菜单，需要校验父菜单是否存在
                .eq(menuDTO.getParentId() != 0,
                        Menu::getId, menuDTO.getParentId());
        long count = count(wrapper);
        AssertUtil.isTrue(count != 0, ResultCode.MENU_IS_NOT_EXISTS);
    }

    private void checkMenuExists(MenuDTO menuDTO) {
        // 修改菜单时，确保菜单ID是存在的
        if (menuDTO.getId() != null) {
            // 校验菜单是否已经存在
            Menu menu = getById(menuDTO.getId());
            AssertUtil.isTrue(menu != null, ResultCode.MENU_IS_NOT_EXISTS);
            // 在修改菜单时，确保菜单的类型不能修改
            AssertUtil.isTrue(menu.getType().equals(menuDTO.getType()), ResultCode.MENU_TYPE_IS_NOT_SUPPORT);
        }
        // 新增菜单或修改菜单时，确保父菜单ID是存在的
        LambdaQueryWrapper<Menu> wrapper = Wrappers.<Menu>lambdaQuery()
                // 如果传入的父菜单ID不为0，说明是子菜单，需要校验父菜单是否存在
                .eq(menuDTO.getParentId() != 0,
                        Menu::getId, menuDTO.getParentId());
        long count = count(wrapper);
        AssertUtil.isTrue(count != 0, ResultCode.MENU_IS_NOT_EXISTS);
        // 校验菜单名称是否已经存在
        wrapper = Wrappers.<Menu>lambdaQuery()
                .eq(Menu::getName, menuDTO.getName())
                .ne(menuDTO.getId() != null, Menu::getId, menuDTO.getId());
        Menu menu = getOne(wrapper);
        AssertUtil.isTrue(menu == null, ResultCode.MENU_ALREADY_EXISTS);
    }

    private void setChildren(MenuVO menuVO) {
        List<MenuVO> menuVOS = menuMapper.queryMenuVOsByParentId(menuVO.getId());
        menuVO.setChildren(menuVOS);
    }

}




